前言
本篇将对Android GUI系统SurfaceFlinger(简称SF)合成layer的具体过程进行分析。合成过程是SF最核心的任务,这个过程贯穿了整个SF业务逻辑,SF所有的工作都最终是为了合成显示做准备。所以,了解SF的合成过程对于我们进一步了解Android GUI系统是必不可少的一部分。
VSYNC控制合成
在介绍SF的VSYNC信号控制同步的篇节中,我们分析了VSYNC信号在绘制和合成过程中所发挥的作用,在SF的init方法中,我们创建了合成延时源并通过EventThread管理该延时源,同时我们通过SF的MessageQueue来为SF创建一个监听合成延时源的Connection并将其注册到EventThread的监听者集合中,这样当合成延时源接收到VSYNC信号后通知SF MessageQueue,然后MessageQueue将该事件通知给SF,随后就可以开始进行合成过程了。这之间MessageQueue可以看作是SF的“秘书”,它负责接收一些SF的消息事件。
1 | void SurfaceFlinger::init() { |
这里mSFEventThread为管理合成延时源的EventThread,mEventQueue为SF的MessageQueue,这里通过其setEventThread将其注册为EventThread的监听者,负责接收来自合成延时源的VSYNC事件。
1 | //frameworks/native/services/surfaceflinger/MessageQueue.cpp |
这里通过EventThead的createEventConnection方法创建一个Connection,这个Connection在第一次引用时被注册到EventThread的监听者队列中,随后获取到Connection对应的BitTube,然后将BitTube的文件描述符添加到MessageQueue的Looper中监听起来,监听的回调为MessageQueue::cb_eventReceiver,这样当EventThread通过Connection通知VSYNC信号到达时可以触发回调通知MessageQueue。
1 | //当监听到Connection对应的BitTube的文件描述符有事件到达时,这个回调方法被Looper触发 |
cb_eventReceiver被触发后调用eventReceiver处理VSYNC信号事件,这里先通过DisplayEventReceiver::getEvents方法读取到事件信息。然后对VSYNC事件进行处理,这里INVALIDATE_ON_VSYNC被定义为1,所以通过mHandler的dispatchInvalidate方法进行处理。mHandler是MessageQueue内部使用的Handler。
1 | void MessageQueue::Handler::dispatchRefresh() { |
MessageQueue内部的Handler定义了三个dispatch方法,dispatchRefresh,dispatchInvalidate,dispatchTransaction,对应的在SF一端会通过onMessageReceived分别处理这三个事件,合成的任务也是在其中做处理的。
1 | void SurfaceFlinger::onMessageReceived(int32_t what) { |
在SF的onMessageReceived方法中分别处理TRANSACTION,INVALIDATE以及REFRESH事件,TRANSACTION事件主要是处理Layer和Display的属性变更,这些变更更可能影响到图层可见区域及脏区域的计算。在INVALIDATE事件中除了处理TRANSACTION事件的内容外,还需要获取合成图层layer的最新帧数据,同时还要根据内容更新脏区域。REFRESH事件中主要是合并和渲染输出的处理。实际上我们可以看到,在INVALIDATE事件中包含了TRANSACTION和REFRESH事件的内容,它会完整的处理一次合并和渲染输出过程。大多数情况下SF的任务也是处理INVALIDATE事件。所以接下来我们分析的重点就从INVALIDATE事件开始。
触发合成的时机
SF合成机制依赖于VSYNC信号,但显示设备并不是任何时候都会去进行合成操作,显然,当显示设备的layer没有发生任何变化时候就不需要也不应该去让SF合成,只有当layer内部发生了变化,如最常见的应用绘制好了新的一帧数据,这时候会通过Layer BufferQueue的消费者接口onFrameAvaliable通知SF有新的一帧数据,这时候需要对layer进行合成渲染就需要去请求VSYNC信号,EventThread根据请求触发合成延时源的VSYNC信号通知给监听者也就是上面的MessageQueue,MessageQueue会通知SF去及逆行合成操作。
1 | //frameworks/native/services/surfaceflinger/Layer.cpp |
有新的一帧数据准备好了,通过SF通知MessageQueue安排一次合成操作。
1 | // |
通过MessageQueue的invalidate方法请求一次VSYNC信号。
1 | ////frameworks/native/services/surfaceflinger/MessageQueue.cpp |
invalidate会通过requestNextVsync请求一次VSYNC信号,这个会通过合成延时源的EventThread进行,当VSYNC信号到达后,会通知MessageQueue注册的监听者,从而将VSYNC信号事件传递给MessageQueue,MessageQueue最终告知SF进行合成操作。
处理Layer及Display属性变更
handleMessageTransaction的任务是处理Layer及Display的属性变更,这个什么意思呢?在Layer内部有实际上有两个State对象mCurrentState和mDrawingState,都维护着Layer的状态信息,当用户调用Layer的方法对其属性如大小,透明度等做了更改后
,设置的值是保存在mCurrentState这个对象中,而mDrawingState是当前正在使用的状态,这样做的目的是不会因为用户的更改而影响到Layer的绘制合成过程。所以在handleMessageTransaction方法中会计算相关Layer的属性变更,这些变更可能影响到后续可见区域的计算。
1 | //frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp |
handleMessageTransaction通过peekTransactionFlags查看transaction flag,这个标记决定是否需要进行transaction
,如果需要调用handleTransaction进一步通过调用handleTransactionLocked方法处理。
1 | //frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp |
在SF内部也维护了两个State状态,mCurrentState和mDrawingState,它们同Layer内部的State定义是不同,mDrawingState是SF上次合成使用的绘图状态,而mCurrentState是SF当前最新的绘图状态。
1 | struct State { |
在State的内部包含了一个LayerVector对象layersSortedByZ,从名称上看它是一个以Z序排序的layer集合。这个集合中的layer就是SF即将要使用进行合成的layer。另一个成员displays描述了显示设备的状态信息,它实际上一个Map,key值为设备的Token,也是一个IBinder,value为DisplayDeviceState代表显示设备的状态。
在handleTransactionLocked方法中,我们先从当前状态中取出Layer集合,然后针对每个layer进行doTransaction处理,这里面会对layer的属性变更做处理,我们后面再分析,根据其处理结果,我们可以直到是否影响到可见区域,如果影响到则标记
mVisibleRegionsDirty为true。随后处理显示设备的变更,为什么会在这里处理我想可能是为了支持显示设备的热插拔。通过对比前后两次设备状态,可以知道设备是被移除了还是添加了,或者是设备的其他信息发生了变化等等,针对这些做不同的处理。
设备的增加和删除都会分别从SF的mDisplays进行相应的增加和删除操作,mDisplays是一个DefaultKeyedVector< wp
接下来处理transform hint的变更,transform hint被用来提高layer的系统性能,关于这个在本篇中不做介绍,略过。
最后的部分是处理layer的变更,前后两次的绘制合成过程,可能有新的layer添加进来,这时候我们同样需要置mVisibleRegionsDirty为true,表示可见区域的变化,但也有可能之前使用的layer被移除,那么它之前的显示区域也就成了脏区域,需要进行更新,这个是通过invalidateLayerStack处理的。随后通过commitTransaction提交transaction,将mCurrentState赋值给mDrawingState
1 | void SurfaceFlinger::commitTransaction() |
layer的属性变更
1 | //处理layer的变更 |
layer的属性变更也是通过比较mDrawingState和mCurrentState进行计算的,最后同样会通过commitTransaction提交transaction。在这里会对影响可视区域计算的属性做了Layer::eVisibleRegion标记。
获取Layer的帧数据以及计算显示设备的脏区域
1 | void SurfaceFlinger::handleMessageInvalidate() { |
更新完transcation后接下来就是获取layer显示数据,这个是通过layer的latchBuffer方法获取到的,通过latchBuffer可以得知layer的可见区域,这个可见区域就是显示设备需要更新的脏区域,脏区域通过invalidateLayerStack计算。
合成过程分析
合成主要流程
1 | void SurfaceFlinger::handleMessageRefresh() { |
preComposition
合成的过程主要是在handleMessageRefresh中进行的,我们分别介绍,先看preComposition
1 | //合成前的预处理 |
preComposition是合成的预处理部分,这部分是判断合成前是否有layer还有新的帧数据mQueuedFrames > 0,如果有的话就需要通过signalLayerUpdate再安排一次合成操作。
rebuildLayerStacks
1 | //重建设备的可见Layer集合,并计算每个layer的可见区域和脏区域 |
rebuildLayerStacks负责重建设备的可见Layer集合,并计算每个layer的可见区域和脏区域。每个显示设备都有一个可见layer集合,这个layer集合将最终被合成在显示设备上。rebuildLayerStacks针对每一个显示设备都进行处理,通过computeVisibleRegions为每个显示设备计算layer可见区域和脏区域,当layer的可见区域和设备窗口区域有交集说明该layer可以显示在该设备中,添加到其可见的layer集合layersSortedByZ中,最后通过setVisibleLayersSortedByZ将该集合设置给显示设备,同时更新显示设备的脏区域dirtyRegion。
setUpHWComposer
1 | void SurfaceFlinger::setUpHWComposer() { |
setUpHWComposer负责为显示设备创建workList,为每个设备要输出显示的layer设置frame data,最后由HWC的prepare确定显示设备可见layer的合成方式,下面我们详细的分析这些内容。
创建WorkList
1 | //frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer.cpp |
每个显示设备都有一个DisplayData对象用来描述显示数据。DisplayData的定义如下:
1 | struct DisplayData { |
其中DisplayData的hwc_display_contents_1用来存放即将要显示到设备的layer,这个结构的定义如下
1 | //这个结构描述的是输出到显示设备的内容 |
hwc_display_contents_1内部指定了要显示的layer的个数,这些layer是通过hwc_layer_1_t数组进行描述的。我们要创建的workList就是为hwc_display_contents_1以及其内部的hwc_layer_1_t数组分配内存用来存放即将要显示的layer信息。在createWorkList中,如果设备版本为HWC_DEVICE_API_VERSION_1_1,说明支持frameBufferTarget,需要额外的创建多一个hwc_layer_1_t,这个hwc_layer_1_t用来存放的是GLES合成后的layer的信息,它的合成类型被指定为HWC_FRAMEBUFFER_TARGET,在DisplayData中是以framebufferTarget描述的。它实际上是hwc_display_contents_1的hwLayers成员的最后一个hwc_layer_1_t。也就是说hwLayers包含了要合成的layer及合成后的layer的信息。
为layer设置帧数据
为显示设备创建完workList,这时候它只是有了容纳layer的结构体,我们还要告知它每个layer的帧数据,这样显示设备才知道layer如何获取这些数据并进行合成显示。这个是通过Layer的setPerFrameData处理的,还记得之前我们通过Layer的latchBuffer获取了layer最新的帧数据,它被放在mActiveBuffer中,这时候我们就可以将这个最新的帧数据的handle设置到显示设备的layer中了。
1 | //为Layer设置当前帧数据 |
1 | //HWCLayerVersion1实现 |
确定layer的合成方式
setUpHWComposer的最后一步是通过HWComposer的prepare确定显示设备layer的合成方式。
1 | status_t HWComposer::prepare() { |
prepare将所有显示设备的hwc_display_contents_1防止在mLists数组中,然后通过hwc硬件的prepare方法决定每个显示设备的layer是否支持硬件合成,如果是就将其compositionType标记为HWC_OVERLAY,默认情况下compositionType是HWC_FRAMEBUFFER表示通过GLES合成。最后通过处理结果来更新DisplayData的hasFbComp和hasOvComp,它们分别表示是否有GLES合成和硬件合成的layer。
doComposition
1 | void SurfaceFlinger::doComposition() { |
doCompositiont负责处理那些需要进行GLES合成的layer。最后通过postFramebuffer提交给硬件合成模块进行合成显示。GLES合成layer是通过doDisplayComposition处理的,我们先看它的实现:
1 | void SurfaceFlinger::doDisplayComposition(const sp<const DisplayDevice>& hw, |
doDisplayComposition又通过doComposeSurfaces合成显示设备的layer,之前我们为设备创建了workList,知道合成的layer最终会被保存在frameBufferTarget对应的hwc_layer_1_t中,那么这到底是怎么样实现呢?我们接着看doComposeSurfaces的实现。
1 | void SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& hw, const Region& dirty) |
在doComposeSurfaces方法中,我们首先通过DisplayDevice的makeCurrent方法配置EGL display和context,要合成的对象最终被渲染到DisplayDevice的本地窗口中,后面我们对此进行分析,随后取到设备的可见layer集合,然后通过循环遍历这个集合,在循环中我们只需要关心通过GLES合成部分的layer,这些layer的合成会调用draw方法进行处理。
1 | //Layer进行软件合成 |
Layer通过draw合成渲染之前,我们通过DisplayDevice的makeCurrent已经配置好了EGL,在onDraw方法中先通过bindTextureImage将当前layer的buffer绑定到GL纹理,这个是通过GLConsumer的bindTextureImageLocked实现的,因为这里的mSurfaceFlingerConsumer它是个SurfaceFlingerConsumer,继承自GLConusmer。
1 | status_t GLConsumer::bindTextureImageLocked() { |
在bindTextureImaageLocked中glBindTexture绑定渲染的纹理,其中mTexName为纹理ID,这个纹理ID在Layer构造的时候就生成了,渲染的纹理mTexture也是在Layer构造的时候进行初始化的,它是通过SF创建的RenderEngine创建的纹理ID mTextureName,该纹理ID被传递给了Layer的消费者GLConsumer。
1 | Layer(...){ |
1 | class SurfaceFlingerConsumer : public GLConsumer { |
在SurfaceFlingerConsumer的构造中将纹理ID传递给GLConsumer,GLConsumer将其保存在成员mTexName,所以layer通过onDraw渲染的纹理和消费者SurfaceFlingerConsumer使用的是同一个纹理。
前面我们知道Layer渲染纹理前会通过DisplayDevice通过makeCurrent配置EGL,我们看看DisplayDevice是如何创建为EGL创建本地窗口的。
1 | DisplayDevice::DisplayDevice( |
在SF初始化显示设备的时候会为其创建DisplayDevice对象,这个对象内部会创建EGL本地窗口EGLSurface mSurface,它内部使用的BufferQueue和其参数mDisplaySurface指定的FrameBufferSurface使用同一个BufferQueue,这里又是一个生产-消费模型,本地窗口Surface负责成产数据,而FrameBufferSurface负责对数据进行消费。具体来说就是layer渲染的纹理数据最终是交给EGL本地窗口了,而本地窗口和FrameBufferSurface使用同一个BufferQueue,FrameBufferSurface作为BufferQueue的消费端最终会接收来自于DisplayDevice本地窗口的Buffer数据,这个是通过DisplayDevice的swapBuffer触发的,swapBuffer会使本地窗口的Buffer数据通过BufferQueue的queueBuffer入队,这样就能触发FrameBufferSurface的onFrameAvaliable回调。
1 | //frameworks/native/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp |
通过EGL合成好的数据最终会通过FramebufferSurface的onFrameAvaliable回调消费,这里先通过nexBuffer取到合成好的GraphicBuffer的数据,然后通过HWComposer的fbPost方法将取到的GraphicBuffer保存到设备的framebufferTarget中。
1 | status_t FramebufferSurface::nextBuffer(sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence) { |
nextBuffer通过acquireBufferLocked取到BufferItem,然后从item中取到对应的槽索引mCurrentBufferSlot,最后根据该索引取到对应的GraphicBuffer。
1 | //将合成好的Buffer保存在DisplayData 的frameBufferTarget成员中 |
fbPost将取到的GraphicBuffer通过setFramebufferTarget保存到相应设备的framebufferTarget中,这样就完成设备layer GLES合成的内容。
postFramebuffer
在doComposition的最后一步是将设备的合成好的layer和需要硬件合成的layer一起提交给硬件合成模块,让其进行最终的显示。这个是通过postFramebuffer完成的。
1 | //通知硬件合成模块进行合成 |
最终通过HWComposer的commit方法,将设备数和hwc_display_contents_1指针数据一起传递给硬件的合成模块,hwc_display_contents_1包含了通过GLES合成的layer,它的信息保存hwLayers的最后一个hwc_layer_l_t中,同时hwLayers还可能有需要进行硬件合成的layer。不管怎么样,它们都是一起提交给硬件合成模块的,硬件负责对这些layer进行最终的合成渲染。